#include <algorithm>
#include <fstream>
#include <iostream>
#include <functional>
#include <memory>
#include "Buffer/Array.h"
#include "Common/CommonTool.h"
#include "Directory/Directory.h"
#include "DateTime/DateTime.h"
#include "Encoding/GBK.h"
#include "Encoding/Unicode.h"
#include "Encoding/UTF8.h"
#include "File/File.h"
#include "Log/SystemLog.h"
#include "ModuleLoader/ModuleConfig.h"
#include "TyzLibCurl.h"

using namespace SystemTool::Buffer;
using namespace SystemTool::Clock;
using namespace SystemTool::Encoding;
using namespace SystemTool::IO;

REGISTER_MODULE(ITyzLibCurl, TyzLibCurl)

TyzLibCurl::Boolean TyzLibCurl::m_bIsInit = false;

static TyzLibCurl::GlobalCurl g_GlobalCurl;

static std::string ContentTypeArray[] = {
	"application/json",
	"application/x-www-form-urlencoded",
	"multipart/form-data",
	"text/xml",
	"text/html",
	"application/octet-stream"
};

static std::string ContentCharSetArray[] = {
	"charset=UNICODE",
	"charset=UTF-8",
	"charset=GB2312",
	"charset=GBK"
};

// Construct the TyzLibCurl
TyzLibCurl::TyzLibCurl() :
	m_hFtpHandle(NULL),
	m_bIsFtpUpload(false),
	m_dUploadTotalSize(0.0),
	m_pHeadList(NULL),
	m_iTimeoutS(60),
	m_iTryCount(3),
	m_iErrorCode(CURLE_OK),
	m_strErrorMsg(""),
	m_bDisposed(false)
{
	Initialize();
}

// Detructe the TyzLibCurl
TyzLibCurl::~TyzLibCurl()
{
	Destory();
}

// Initialize the TyzLibCurl
TyzLibCurl::None TyzLibCurl::Initialize()
{

}

// Destory the TyzLibCurl
TyzLibCurl::None TyzLibCurl::Destory()
{
	if (!GetDisposed())
	{
		SetDisposed(true);

		// Clear the head list
		HeadList pHeadList_Old = GetHeadList();

		if (pHeadList_Old)
		{
			ClearRequeHeadHead(pHeadList_Old);
		}

		// Destory the curl
		DestoryCurl();
	}
}

// Init the curl
TyzLibCurl::None TyzLibCurl::InitCurl()
{
	if (!GetIsInit())
	{
		RetCode iRetCode = curl_global_init(CURL_GLOBAL_ALL);

		if (iRetCode == CURLE_FAILED_INIT)
		{
			SetIsInit(false);
		}
		else
		{
			SetIsInit(true);
		}
	}
}

// Destory the curl
TyzLibCurl::None TyzLibCurl::DestoryCurl()
{
	if (GetIsInit())
	{
		curl_global_cleanup();

		SetIsInit(false);
	}
}

// Set time out
void TyzLibCurl::SetTimeout(int iSeconds)
{
	SetTimeoutS(iSeconds);
}

// Set try count
void TyzLibCurl::SetTryCount(int iCount)
{
	SetTryCnt(iCount);
}

// Set upload callback function
void TyzLibCurl::SetUploadFunc(UploadProgress pUploadFunc, void* pUserData)
{
	SetUploadFunction(pUploadFunc);

	SetUserData(pUserData, true);
}

// Set download callback function
void TyzLibCurl::SetDownloadFunc(DownLoadProgress pDownloadFunc, void* pUserData)
{
	SetDownloadFunction(pDownloadFunc);

	SetUserData(pUserData, false);
}

// Get the error std::string
void TyzLibCurl::GetErrorInfo(int& iErrorCode, std::string& strErrorMsg)
{
	iErrorCode = GetErrorCode();

	strErrorMsg = GetErrorMsg();
}

// Get the current TyzLibCurl version
std::string TyzLibCurl::GetCurVersion()
{
	if (!GetIsInit())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "TyzLibCurl is not initialized!");

		return "";
	}

	std::string strVersion = curl_version();

	return strVersion;
}

// Is Ftp curl valid
TyzLibCurl::Boolean TyzLibCurl::IsFtpCurlValid()
{
	if (m_hFtpHandle)
	{
		return true;
	}

	return false;
}

// Create Curl
TyzLibCurl::Boolean TyzLibCurl::CreateUrL(UrlHandle& pHandle)
{
	if (pHandle == NULL)
	{
		pHandle = curl_easy_init();
	}

	return true;
}

// Destory Curl
TyzLibCurl::None TyzLibCurl::DestoryUrL(UrlHandle pHandle)
{
	if (pHandle)
	{
		curl_easy_cleanup(pHandle);

		pHandle = NULL;
	}
}

// Read data (Called by url inner)
size_t TyzLibCurl::OnReadData(void* buffer,
	size_t size,
	size_t nmemb,
	void* lpVoid)
{
	FILE* pFile = static_cast<FILE*>(lpVoid);

	if (pFile == NULL)
	{
		return CURL_READFUNC_ABORT;
	}

	size_t nReadByte = fread(buffer, size, nmemb, pFile);

	return nReadByte;
}

// Write data (Called by url inner)
size_t TyzLibCurl::OnWriteFileData(void* buffer,
	size_t size,
	size_t nmemb,
	void* lpVoid)
{
	FILE* pFile = static_cast<FILE*>(lpVoid);

	if (pFile == NULL)
	{
		return CURL_READFUNC_ABORT;
	}

	size_t nReadByte = fwrite(buffer, size, nmemb, pFile);

	return nReadByte;
}

// Write data (Called by url inner)
size_t TyzLibCurl::OnWriteData(void* buffer,
	size_t size,
	size_t nmemb,
	std::string* strData)
{
	if (strData == NULL)
	{
		return -1;
	}

	size_t iTotalSize = size * nmemb;

	Array<char> RespondData(static_cast<int>(iTotalSize));

	Array<char>::Copy((char*)buffer, static_cast<int>(iTotalSize), RespondData.Data(), RespondData.Size());

	std::string strFinalData = RespondData.Data();

	strData->append(strFinalData);

	return nmemb;

	/*size_t iTotalSize = size * nmemb;

	static char* pRespondData = NULL;

	if (pRespondData)
	{
		delete[] pRespondData;

		pRespondData = NULL;
	}

	pRespondData = new char[iTotalSize];

	memset(pRespondData, 0, iTotalSize);

	memcpy_s(pRespondData, iTotalSize, buffer, iTotalSize);

	std::string strFinalData = pRespondData;

	strData->append(strFinalData);

	return nmemb;*/
}

// Progerss call back for download and upload
int TyzLibCurl::ProgressCallback(void* pUserData,
	double TotalToDownload,
	double NowDownloaded,
	double TotalToUpload,
	double NowUpload)
{
	TyzLibCurl* pThis = (TyzLibCurl*)pUserData;

	if (pThis == NULL)
	{
		return -1;
	}

	UploadProgress pUploadFunc = pThis->GetUplaodFunction();

	if (pUploadFunc)
	{
		if (pThis->GetIsFtpUpload())
		{
			pUploadFunc(pThis->GetUserData(true),
				pThis->GetUploadTotalSize(),
				NowUpload);
		}
		else
		{
			pUploadFunc(pThis->GetUserData(true),
				TotalToUpload,
				NowUpload);
		}
	}

	DownLoadProgress pDownloadFunc = pThis->GetDownloadFunction();

	if (pDownloadFunc)
	{
		pDownloadFunc(pThis->GetUserData(false),
			TotalToDownload,
			NowDownloaded);
	}

	return 0;
}

// Set error info
TyzLibCurl::None TyzLibCurl::SetErrorInfo(RetCode eRetCode, std::string strErrorMsg)
{
	SetErrorCode(eRetCode);

	SetErrorMsg(strErrorMsg);
}

// Get error info
std::string TyzLibCurl::GetCurlErrorInfo(RetCode eRetCode)
{
	std::string strErrorMsg = curl_easy_strerror(eRetCode);

	std::string strData = GBK::GetString(strErrorMsg, ENCODE_UTF8);

	return strData;
}

// Clear the head list
TyzLibCurl::None TyzLibCurl::ClearRequeHeadHead(HeadList& hList)
{
	if (hList)
	{
		curl_slist_free_all(hList);

		hList = NULL;
	}
}

// Is can do the operation
TyzLibCurl::Boolean TyzLibCurl::IsCanOperate()
{
	if (!GetIsInit())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "TyzLibCurl is not initialized yet !");

		return false;
	}

	return true;
}

// Set head for url
TyzLibCurl::None TyzLibCurl::SetRequestHead(ContentTypeEnum eContentType, ContentChartSetEnum eCharSet)
{
	// Clear the head list
	HeadList pHeadList_Old = GetHeadList();

	if (pHeadList_Old)
	{
		ClearRequeHeadHead(pHeadList_Old);
	}

	std::string strHeadInfo = std::string("Content-Type")
		+ ":"
		+ ContentTypeArray[eContentType]
		+ ";"
		+ ContentCharSetArray[eCharSet];

	HeadList pHeadList = NULL;

	pHeadList = curl_slist_append(pHeadList, strHeadInfo.c_str());

	if (pHeadList == NULL)
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Head list is empty!");

		return;
	}

	SetHeadList(pHeadList);
}

// Get the respoend by http or https (pCaPath==NULL : do not verify the certification on server)
bool TyzLibCurl::Get(std::string strRequestUrl,
	std::string& strResponseData,
	std::map<std::string, std::string>* pRequestMap,
	const char* pCaPath)
{
	if (strRequestUrl.empty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Input url is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Create an url
	UrlHandle pHandle = NULL;

	if (!CreateUrL(pHandle))
	{
		return false;
	}

	SetRequestHead(APP_FORM_ENCODE, CHARSET_UTF8);
	
	curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(pHandle, CURLOPT_HTTPGET, 1);

	std::string strFinalEncodeData = "?";

	if (pRequestMap)
	{
		if (!pRequestMap->empty())
		{
			for (std::map<std::string, std::string>::iterator Iter = pRequestMap->begin();
				Iter != pRequestMap->end();
				++Iter)
			{
				std::string strKey = Iter->first;

				std::string strValue = Iter->second;
				
				char* pEncodeData = curl_easy_escape(pHandle, strValue.c_str(), strValue.length());

				std::string strEncodeData = pEncodeData;
				
				curl_free(pEncodeData);

				strFinalEncodeData = strFinalEncodeData + strKey + "=" + strEncodeData + "&";
			}

			if (strFinalEncodeData[strFinalEncodeData.length()-1] == '&')
			{
				strFinalEncodeData = strFinalEncodeData.substr(0, strFinalEncodeData.length() - 1);
			}
		}
	}

	std::string strUrl = strRequestUrl;

	if (strFinalEncodeData != "?")
	{
		strUrl = strUrl + strFinalEncodeData;
	}
	
	curl_easy_setopt(pHandle, CURLOPT_URL, (char*)(strUrl.c_str()));

	curl_easy_setopt(pHandle, CURLOPT_FOLLOWLOCATION, 1);

	curl_easy_setopt(pHandle, CURLOPT_READFUNCTION, NULL);

	curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, OnWriteData);

	std::string strRespond = "";

	curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, &strRespond);

	curl_easy_setopt(pHandle, CURLOPT_NOSIGNAL, 1);

	curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT, 3);

	curl_easy_setopt(pHandle, CURLOPT_TIMEOUT, GetTimeoutS());

	{
		curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, false);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, (void*)this);
	}

	if (pCaPath == NULL)
	{
		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYPEER, false);

		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYPEER, true);

		curl_easy_setopt(pHandle, CURLOPT_CAINFO, pCaPath);
	}
	
	CURLcode eRetCode = curl_easy_perform(pHandle);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(pHandle);

		return false;
	}

	// Change the respond data encode
	strResponseData = strRespond;

	// Destory the url
	DestoryUrL(pHandle);

	return true;
}

// Post the request by https (pCaPath==NULL : do not verify the certification on server)
bool TyzLibCurl::Post(std::string strRequestUrl,
	std::string strRequestJsonData,
	std::string& strResponseData,
	const char* pCaPath)
{
	if (strRequestUrl.empty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Input url is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Create an url
	UrlHandle pHandle = NULL;

	if (!CreateUrL(pHandle))
	{
		return false;
	}

	curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(pHandle, CURLOPT_URL, (char*)(strRequestUrl.c_str()));

	curl_easy_setopt(pHandle, CURLOPT_POST, 1);

	curl_easy_setopt(pHandle, CURLOPT_POSTFIELDS, (char*)strRequestJsonData.c_str());

	curl_easy_setopt(pHandle, CURLOPT_POSTFIELDSIZE, strRequestJsonData.size());

	curl_easy_setopt(pHandle, CURLOPT_READFUNCTION, NULL);

	curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, OnWriteData);

	std::string strRespond = "";

	curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, &strRespond);

	curl_easy_setopt(pHandle, CURLOPT_NOSIGNAL, 1);

	{
		curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, false);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
	}

	if (pCaPath == NULL)
	{
		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYPEER, false);

		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYPEER, true);

		curl_easy_setopt(pHandle, CURLOPT_CAINFO, pCaPath);
	}

	curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT, 3);

	curl_easy_setopt(pHandle, CURLOPT_TIMEOUT, GetTimeoutS());

	CURLcode eRetCode = curl_easy_perform(pHandle);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(pHandle);

		return false;
	}

	// Change the respond data encode
	strResponseData = strRespond;

	// Destory the url
	DestoryUrL(pHandle);

	return true;
}

// Post the request by https (pCaPath==NULL : do not verify the certification on server)
bool TyzLibCurl::Post(std::string strRequestUrl,
	std::string& strResponseData,
	std::map<std::string, std::string>* pRequestMap,
	const char* pCaPath)
{
	if (strRequestUrl.empty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Input url is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Create an url
	UrlHandle pHandle = NULL;

	if (!CreateUrL(pHandle))
	{
		return false;
	}

	curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(pHandle, CURLOPT_URL, (char*)(strRequestUrl.c_str()));

	curl_easy_setopt(pHandle, CURLOPT_HTTPPOST, 1);

	std::string strFinalEncodeData = "";

	if (pRequestMap)
	{
		if (!pRequestMap->empty())
		{
			for (std::map<std::string, std::string>::iterator Iter = pRequestMap->begin();
				Iter != pRequestMap->end();
				++Iter)
			{
				std::string strKey = Iter->first;

				std::string strValue = Iter->second;

				char* pEncodeData = curl_easy_escape(pHandle, strValue.c_str(), strValue.length());

				std::string strEncodeData = pEncodeData;

				curl_free(pEncodeData);

				strFinalEncodeData = strFinalEncodeData + strKey + "=" + strEncodeData + "&";
			}

			if (strFinalEncodeData[strFinalEncodeData.length() - 1] == '&')
			{
				strFinalEncodeData = strFinalEncodeData.substr(0, strFinalEncodeData.length() - 1);
			}
		}
	}

	if (strFinalEncodeData != "")
	{
		std::string strPara = strFinalEncodeData;

		curl_easy_setopt(pHandle, CURLOPT_POSTFIELDS, (char*)(strFinalEncodeData.c_str()));
	}

	curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, OnWriteData);

	std::string strRespond = "";

	curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, &strRespond);

	curl_easy_setopt(pHandle, CURLOPT_NOSIGNAL, 1);

	{
		curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, false);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
	}

	if (pCaPath == NULL)
	{
		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYPEER, false);

		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(pHandle, CURLOPT_SSL_VERIFYPEER, true);

		curl_easy_setopt(pHandle, CURLOPT_CAINFO, pCaPath);
	}

	curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT, 3);

	curl_easy_setopt(pHandle, CURLOPT_TIMEOUT, GetTimeoutS());

	CURLcode eRetCode = curl_easy_perform(pHandle);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(pHandle);

		return false;
	}

	// Change the respond data encode
	strResponseData = strRespond;

	// Destory the url
	DestoryUrL(pHandle);

	return true;
}

// Upload file (application/octet-stream)
bool TyzLibCurl::UploadFile(std::string strRequestUrl,
	std::vector<std::string>& LocalFileTable,
	std::string& strRespondData,
	const char* pCaPath)
{
	if (strRequestUrl.empty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Input url is empty !");

		return false;
	}

	if (LocalFileTable.empty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Input file table is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Create an url
	UrlHandle pHandle = NULL;

	if (!CreateUrL(pHandle))
	{
		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	struct curl_httppost* formpost = NULL;

	struct curl_httppost* lastptr = NULL;

	// Set the form table here
	int iFileCount = static_cast<int>(LocalFileTable.size());

	for (int index = 0; index < iFileCount; ++index)
	{
		std::string strFilePath = LocalFileTable[index];

		curl_formadd(&formpost, &lastptr,
		CURLFORM_COPYNAME, "uploadimg",
		CURLFORM_FILE, strFilePath.c_str(),
		CURLFORM_CONTENTTYPE, "application/octet-stream",
		CURLFORM_END);
	}

	curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(pHandle, CURLOPT_URL, strRequestUrl.c_str());

	curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, OnWriteData);

	curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, &strRespondData);

	curl_easy_setopt(pHandle, CURLOPT_POST, 1L);

	curl_easy_setopt(pHandle, CURLOPT_VERBOSE, 1L);

	curl_easy_setopt(pHandle, CURLOPT_HEADER, 1L);

	curl_easy_setopt(pHandle, CURLOPT_HTTPPOST, formpost);

	curl_easy_setopt(pHandle, CURLOPT_FOLLOWLOCATION, 1L);

	// Set callback progress
	{
		curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, false);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
	}

	CURLcode eRetCode = curl_easy_perform(pHandle);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		curl_formfree(formpost);

		// Destory the url
		DestoryUrL(pHandle);

		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	curl_formfree(formpost);

	// Destory the url
	DestoryUrL(pHandle);

	SetUploadTotalSize(0.0);

	SetIsFtpUpload(false);

	return true;
}

// Download file (application/octet-stream)
bool TyzLibCurl::DownloadFile(std::string strRequestUrl,
	std::string strLocalSaveFilePath,
	const char* pCaPath)
{
	if (strRequestUrl.empty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Input url is empty !");

		return false;
	}

	if (strLocalSaveFilePath.empty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Local save file path is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Create an url
	UrlHandle pHandle = NULL;

	if (!CreateUrL(pHandle))
	{
		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	// Set resume mode
	struct stat CurFileInfo;

	Int32 iUseResume = 0;

	curl_off_t iLocalFileLength = -1;

	if (stat(strLocalSaveFilePath.c_str(), &CurFileInfo) == 0)
	{
		iLocalFileLength = CurFileInfo.st_size;

		iUseResume = 1;
	}
	else
	{
		// Get directory from the file path
		std::string strDirPath = Directory::GetDirBy(strLocalSaveFilePath);

		if (strDirPath == "")
		{
			SetErrorInfo(CURLE_UNKNOWN_OPTION, "Failed to get directory of the local file!");

			return false;
		}

		// Create local file at first
		if (!Directory::Create(strDirPath))
		{
			SetErrorInfo(CURLE_UNKNOWN_OPTION, "Failed to create directory for the local file!");

			return false;
		}
	}

	// Open local file
	FILE* pFile = NULL;

	fopen_s(&pFile, strLocalSaveFilePath.c_str(), "ab+");

	if (pFile == NULL)
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Failed to open local file!");

		return false;
	}

	curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(pHandle, CURLOPT_URL, strRequestUrl.c_str());

	curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT, GetTimeoutS());

	curl_easy_setopt(pHandle, CURLOPT_RESUME_FROM_LARGE, iUseResume ? iLocalFileLength : 0);

	curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, OnWriteFileData);

	curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, pFile);

	curl_easy_setopt(pHandle, CURLOPT_FOLLOWLOCATION, 1L);

	// Set the progress callback
	{
		curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, false);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);

		curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
	}

	CURLcode eRetCode = CURLE_GOT_NOTHING;

	int iTryCount = GetTryCnt();

	for (Int32 index = 0; (eRetCode != CURLE_OK) && (index < iTryCount); ++index)
	{
		if (iUseResume)
		{
			curl_easy_setopt(pHandle, CURLOPT_NOBODY, 1L);

			curl_easy_setopt(pHandle, CURLOPT_HEADER, 1L);

			eRetCode = curl_easy_perform(pHandle);

			if (eRetCode != CURLE_OK)
			{
				continue;
			}

			curl_easy_setopt(pHandle, CURLOPT_NOBODY, 0L);

			curl_easy_setopt(pHandle, CURLOPT_HEADER, 0L);

			fseek(pFile, static_cast<long>(iLocalFileLength), SEEK_SET);

			curl_easy_setopt(pHandle, CURLOPT_APPEND, 1L);
		}
		else
		{
			curl_easy_setopt(pHandle, CURLOPT_APPEND, 0L);
		}

		eRetCode = curl_easy_perform(pHandle);
	}

	// Finish the uploading
	fclose(pFile);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(pHandle);

		return false;
	}

	// Destory the url
	DestoryUrL(pHandle);

	return true;
}

// Get content length
size_t TyzLibCurl::GetContentLength(Object pHeader,
	size_t iHeadSize,
	size_t iMem,
	Object pStream)
{
	long iLength = 0;

	Int32 iRet = sscanf_s(static_cast<const char*>(pHeader), "Content-Length: %ld\n", &iLength);

	if (iRet)
	{
		*(static_cast<long*>(pStream)) = iLength;
	}

	return iHeadSize * iMem;
}

// Ftp connection
bool TyzLibCurl::FtpConnect(const std::string strRemoteIpAddr,
	const std::string strUserName,
	const std::string strPassword,
	const std::string strPortNo)
{
	if (strRemoteIpAddr.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "remote ip address is empty !");

		return false;
	}

	if (strUserName.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "user name is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Create an url
	if (!CreateUrL(m_hFtpHandle))
	{
		return false;
	}

	// Compose the user name and password
	std::string strUserKey = strUserName + ":" + strPassword;

	// Set user and password
	curl_easy_setopt(m_hFtpHandle, CURLOPT_USERPWD, strUserKey.c_str());

	// Set the url
	std::string strAddr = std::string("ftp://")
		+ strRemoteIpAddr
		+ ":"
		+ strPortNo;

	curl_easy_setopt(m_hFtpHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_URL, strAddr.c_str());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_VERBOSE, 1L);

	CURLcode eRetCode = curl_easy_perform(m_hFtpHandle);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(m_hFtpHandle);

		return false;
	}

	m_FtpInfo.strIpAddr = strRemoteIpAddr;

	m_FtpInfo.strUserName = strUserName;

	m_FtpInfo.strPassword = strPassword;

	m_FtpInfo.iPortNo = atoi(strPortNo.c_str());

	return true;
}

// List ftp directory info (strRemoteDirPath: /Test/12/)
bool TyzLibCurl::FtpList(const std::string strRemoteDirPath, std::vector<FileInfo>& FileInfoTable)
{
	if (strRemoteDirPath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "remote directory path is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Is Ftp curl valid
	if (!IsFtpCurlValid())
	{
		return false;
	}

	if (m_FtpInfo.IsEmpty())
	{
		SetErrorInfo(CURLE_FAILED_INIT, "Please connect to FTP at first !");

		return false;
	}

	std::string strPath = strRemoteDirPath;

	Directory::TransformAsUnixPath(strRemoteDirPath,strPath);

	if (strPath[0] != '/')
	{
		strPath = std::string("/") + strPath;
	}

	if (strPath.find_last_of('/') != strPath.length() - 1)
	{
		strPath = strPath + "/";
	}

	// Build the whole path
	std::string strPortNo = std::to_string(m_FtpInfo.iPortNo);

	std::string strUrl = "ftp://"
		+ m_FtpInfo.strIpAddr
		+ ":"
		+ strPortNo
		+ strPath;

	std::string strResponse = "";

	curl_easy_setopt(m_hFtpHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_URL, strUrl.c_str());

	//curl_easy_setopt(m_hFtpHandle, CURLOPT_FTPPORT, strPortNo.c_str());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_VERBOSE, 1L);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_WRITEFUNCTION, OnWriteData);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_WRITEDATA, &strResponse);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_CUSTOMREQUEST, "MLSD");

	CURLcode eRetCode = curl_easy_perform(m_hFtpHandle);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(m_hFtpHandle);

		return false;
	}

	// Parse the respond data
	if (!GetFileInfo(strPath, strResponse, FileInfoTable))
	{
		return false;
	}

	return true;
}

// Get file info 
bool TyzLibCurl::GetFileInfo(std::string strInfo, std::string& strFileInfo)
{
	if (strInfo.empty())
	{
		return false;
	}

	std::string strInformation = strInfo;

	int iPos = strInformation.find_last_of("=");

	if (iPos == -1)
	{
		return false;
	}

	strFileInfo = strInformation.substr(iPos + 1);

	return true;
}

// Parse the file info
TyzLibCurl::Boolean TyzLibCurl::GetFileInfo(std::string strParentPath, std::string strAllFileInfo, std::vector<FileInfo>& Table)
{
	if (strParentPath.empty())
	{
		strParentPath = "/";
	}

	if (strAllFileInfo.empty())
	{
		return false;
	}

	// Get rows
	std::vector<std::string> InfoTable;

	if (!CommonTool::Split(strAllFileInfo, "\r\n", InfoTable))
	{
		return false;
	}

	if (InfoTable.empty())
	{
		return false;
	}

	bool bIsAllGood = true;

	for (int index = 0; index < static_cast<int>(InfoTable.size()); ++index)
	{
		std::string strData = InfoTable[index];

		if (strData.find("type") == -1)
		{
			continue;
		}

		std::vector<std::string> ItemTable;

		if (!CommonTool::Split(strData, ";", ItemTable))
		{
			bIsAllGood = false;

			continue;
		}

		if (ItemTable.empty())
		{
			bIsAllGood = false;

			continue;
		}

		FileInfo InfoData;

		// Get file type
		{
			std::string strFileType = ItemTable[0];

			std::string strFinalFileType = "";

			if (!GetFileInfo(strFileType, strFinalFileType))
			{
				bIsAllGood = false;

				continue;
			}

			if (strFinalFileType == "file")
			{
				InfoData.bIsDirectory = false;
			}
			else
			{
				InfoData.bIsDirectory = true;
			}
		}

		// Get file modify time
		{
			std::string strModifyTime = ItemTable[1];

			std::string strFileModifyTime = "";

			if (!GetFileInfo(strModifyTime, strFileModifyTime))
			{
				bIsAllGood = false;

				continue;
			}

			std::string strTime = strFileModifyTime;

			std::string strYear = strTime.substr(0, 4);
			
			std::string strMonth = strTime.substr(4, 2);

			std::string strDay = strTime.substr(6, 2);

			std::string strHour = strTime.substr(8, 2);

			std::string strMin = strTime.substr(10, 2);

			std::string strSecond = strTime.substr(12, 2);

			int iHour = atoi(strHour.c_str());

			iHour += 8;

			strHour =std::to_string(iHour);

			DateTime ModifyTime(atoi(strYear.c_str()),
				atoi(strMonth.c_str()),
				atoi(strDay.c_str()),
				atoi(strHour.c_str()),
				atoi(strMin.c_str()),
				atoi(strSecond.c_str()));

			time_t iModifyTime = ModifyTime.ToTime_t();

			InfoData.iFileModifyTime = static_cast<int>(iModifyTime);

			std::string strFinalTime = ModifyTime.ToString();

			InfoData.strFileModifyTime = strFinalTime;
		}

		if (!InfoData.bIsDirectory)
		{
			// Get file size
			std::string strSize = ItemTable[2];

			std::string strFinalFileSize = "";

			if (!GetFileInfo(strSize, strFinalFileSize))
			{
				bIsAllGood = false;

				continue;
			}

			std::string strFinalSize = strFinalFileSize;

			InfoData.iFileSize = atol(strFinalSize.c_str());

			// Get file name and path
			{
				std::string strName = ItemTable[3];

				if (strName[0] == ' ')
				{
					strName = strName.substr(1);
				}

				std::string strFileName = strName;

				InfoData.strFileName = strFileName;

				std::string strPath = strParentPath + InfoData.strFileName;

				InfoData.strFilePath = strPath;
			}
		}
		else
		{
			// Get file name and path
			std::string strName = ItemTable[2];

			if (strName[0] == ' ')
			{
				strName = strName.substr(1);
			}

			std::string strFileName = strName;

			InfoData.strFileName = strFileName;

			std::string strPath = strParentPath + InfoData.strFileName;

			InfoData.strFilePath = strPath;
		}

		Table.push_back(InfoData);
	}

	return bIsAllGood;
}

// Get file info
bool TyzLibCurl::GetFileInfoByName(FileInfo& InInfo, std::string strFileName)
{
	if (strFileName.empty())
	{
		return false;
	}

	if (InInfo.strFileName == strFileName)
	{
		return true;
	}

	return false;
}

// Get file size
bool TyzLibCurl::GetFtpFileInfo(const std::string strRemoteFilePath, FileInfo& Info)
{
	if (strRemoteFilePath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Input remote directory path is empty !");

		return false;
	}

	// Get file's parent directory
	std::string strRemotePath = Directory::TrimEnding(strRemoteFilePath);

	int iPos = strRemotePath.find_last_of('/');

	if (iPos == -1)
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Input remote directory path is invalid !");

		return false;
	}

	std::string strFileName = strRemotePath.substr(iPos + 1);

	std::string strParentDirPath = strRemotePath.substr(0, iPos);

	std::vector<FileInfo> FileInfoTable;

	if (!FtpList(strParentDirPath, FileInfoTable))
	{
		return false;
	}

	std::string strFinalFileName = strFileName;

	std::vector<FileInfo>::iterator Iter = FileInfoTable.end();

	FileInfo FinalInfo;

	Iter = std::find_if(FileInfoTable.begin(),
		FileInfoTable.end(),
		std::bind(&TyzLibCurl::GetFileInfoByName,this, std::placeholders::_1,strFinalFileName));

	if (Iter == FileInfoTable.end())
	{
		return false;
	}

	Info = *Iter;

	return true;
}

// Ftp upload (strRemoteFilePath: /test)
bool TyzLibCurl::FtpUpload(const std::string strRemoteDirPath, const std::string strLocalFilePath)
{
	if (strRemoteDirPath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Input remote directory path is empty !");

		return false;
	}

	if (strLocalFilePath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Local file path is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Is Ftp curl valid
	if (!IsFtpCurlValid())
	{
		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	// Build remote file path
	std::string strRemoteFilePath = "";

	if (!BuildFilePath(strLocalFilePath,strRemoteDirPath, strRemoteFilePath))
	{
		std::string strErrorMsg = std::string("Failed to build the remote file path by local file path:") + strLocalFilePath;

		SetErrorInfo(CURLE_UNKNOWN_OPTION, strErrorMsg);

		return false;
	}

	std::string strLocalPath = strRemoteFilePath;

	Directory::TransformAsUnixPath(strRemoteFilePath, strLocalPath);

	if (strLocalPath[0] != '/')
	{
		strLocalPath = std::string("/") + strLocalPath;
	}

	std::string strPortNo = std::to_string(m_FtpInfo.iPortNo);

	std::string strUrl = "ftp://"
		+ m_FtpInfo.strIpAddr
		+ strLocalPath;

	// Get file size
	struct stat CurFileInfo;

	curl_off_t iFileSize = -1;

	if (stat(strLocalFilePath.c_str(), &CurFileInfo) == 0)
	{
		iFileSize = CurFileInfo.st_size;

		SetUploadTotalSize(static_cast<Real>(iFileSize));

		SetIsFtpUpload(true);
	}

	// Open the local file
	FILE* pFile = NULL;

	fopen_s(&pFile, strLocalFilePath.c_str(), "rb");

	if (pFile == NULL)
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Failed to open local file!");

		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	SetRequestHead(BINARY_STREAM);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_UPLOAD, 1L);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_URL, strUrl.c_str());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_FTPPORT, strPortNo.c_str());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_FTP_RESPONSE_TIMEOUT, GetTimeoutS());

	// Get the file size on the remote server so that we can upload file from break point
	long iFileSizeOnServer = 0;

	{
		curl_easy_setopt(m_hFtpHandle, CURLOPT_HEADERFUNCTION, GetContentLength);

		curl_easy_setopt(m_hFtpHandle, CURLOPT_HEADERDATA, &iFileSizeOnServer);
	}

	curl_easy_setopt(m_hFtpHandle, CURLOPT_WRITEFUNCTION, OnWriteData);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_READFUNCTION, OnReadData);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_READDATA, pFile);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);

	// Set callback progress
	{
		curl_easy_setopt(m_hFtpHandle, CURLOPT_NOPROGRESS, false);

		curl_easy_setopt(m_hFtpHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);

		curl_easy_setopt(m_hFtpHandle, CURLOPT_PROGRESSDATA, this);
	}

	CURLcode eRetCode = CURLE_GOT_NOTHING;

	curl_easy_setopt(m_hFtpHandle, CURLOPT_HEADER, 1L);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_NOBODY, 1L);

	eRetCode = curl_easy_perform(m_hFtpHandle);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Finish the uploading
		fclose(pFile);

		// Destory the url
		DestoryUrL(m_hFtpHandle);

		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	eRetCode = CURLE_GOT_NOTHING;

	curl_easy_setopt(m_hFtpHandle, CURLOPT_NOBODY, 0L);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_HEADER, 0L);

	int iTryCount = GetTryCnt();

	for (Int32 index = 0; (eRetCode != CURLE_OK) && (index < iTryCount); ++index)
	{
		if (iFileSizeOnServer > 0)
		{
			SetUploadTotalSize(static_cast<Real>(iFileSize - iFileSizeOnServer));

			curl_easy_setopt(m_hFtpHandle, CURLOPT_NOBODY, 0L);

			curl_easy_setopt(m_hFtpHandle, CURLOPT_HEADER, 0L);

			fseek(pFile, iFileSizeOnServer, SEEK_SET);

			curl_easy_setopt(m_hFtpHandle, CURLOPT_APPEND, 1L);
		}
		else
		{
			curl_easy_setopt(m_hFtpHandle, CURLOPT_APPEND, 0L);
		}

		eRetCode = curl_easy_perform(m_hFtpHandle);
	}

	// Finish the uploading
	fclose(pFile);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(m_hFtpHandle);

		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	SetUploadTotalSize(0.0);

	SetIsFtpUpload(false);

	return true;
}

// Ftp download (strRemoteFilePath: /test/34.txt)
bool TyzLibCurl::FtpDownload(const std::string strRemoteFilePath, const std::string strLocalDirPath)
{
	if (strLocalDirPath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Input local directory is empty !");

		return false;
	}

	if (strRemoteFilePath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Remote file path is empty !");

		return false;
	}

	// Is can continue operation
	if (!IsCanOperate())
	{
		return false;
	}

	// Is Ftp curl valid
	if (!IsFtpCurlValid())
	{
		SetUploadTotalSize(0.0);

		SetIsFtpUpload(false);

		return false;
	}

	// Build local file path
	std::string strLocalFilePath = "";

	if (!BuildFilePath(strRemoteFilePath, strLocalDirPath, strLocalFilePath))
	{
		std::string strErrorMsg = std::string("Failed to build the local file path by remote file path:") + strRemoteFilePath;

		SetErrorInfo(CURLE_UNKNOWN_OPTION, strErrorMsg);

		return false;
	}

	// Build the whole path to utf8
	std::string strLocalPath = strRemoteFilePath;

	Directory::TransformAsUnixPath(strRemoteFilePath, strLocalPath);

	if (strLocalPath[0] != '/')
	{
		strLocalPath = std::string("/") + strLocalPath;
	}

	std::string strPortNo = std::to_string(m_FtpInfo.iPortNo);

	std::string strUrl = "ftp://"
		+ m_FtpInfo.strIpAddr
		+ ":"
		+ strPortNo
		+ strLocalPath;

	// Set resume mode
	struct stat CurFileInfo;

	Int32 iUseResume = 0;

	curl_off_t iLocalFileLength = -1;

	if (stat(strLocalFilePath.c_str(), &CurFileInfo) == 0)
	{
		iLocalFileLength = CurFileInfo.st_size;

		iUseResume = 1;
	}
	else
	{
		// Get directory from the file path
		std::string strDirPath = Directory::GetDirBy(strLocalFilePath);

		if (strDirPath == "")
		{
			SetErrorInfo(CURLE_UNKNOWN_OPTION, "Failed to get directory of the local file!");

			return false;
		}

		// Create local file at first
		if (!Directory::Create(strDirPath))
		{
			SetErrorInfo(CURLE_UNKNOWN_OPTION, "Failed to create directory for the local file!");

			return false;
		}
	}

	// Open local file
	FILE* pFile = NULL;

	fopen_s(&pFile, strLocalFilePath.c_str(), "ab+");
	
	if (pFile == NULL)
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Failed to open local file!");

		return false;
	}

	curl_easy_setopt(m_hFtpHandle, CURLOPT_HTTPHEADER, GetHeadList());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_URL, strUrl.c_str());

	//curl_easy_setopt(m_hFtpHandle, CURLOPT_FTPPORT, strPortNo.c_str());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_CONNECTTIMEOUT, GetTimeoutS());

	curl_easy_setopt(m_hFtpHandle, CURLOPT_RESUME_FROM_LARGE, iUseResume ? iLocalFileLength : 0);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_WRITEFUNCTION, OnWriteFileData);

	curl_easy_setopt(m_hFtpHandle, CURLOPT_WRITEDATA, pFile);

	// Set the progress callback
	{
		curl_easy_setopt(m_hFtpHandle, CURLOPT_NOPROGRESS, false);

		curl_easy_setopt(m_hFtpHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);

		curl_easy_setopt(m_hFtpHandle, CURLOPT_PROGRESSDATA, this);
	}

	CURLcode eRetCode = CURLE_GOT_NOTHING;

	int iTryCount = GetTryCnt();

	for (Int32 index = 0; (eRetCode != CURLE_OK) && (index < iTryCount); ++index)
	{
		if (iUseResume)
		{
			curl_easy_setopt(m_hFtpHandle, CURLOPT_NOBODY, 1L);

			curl_easy_setopt(m_hFtpHandle, CURLOPT_HEADER, 1L);

			eRetCode = curl_easy_perform(m_hFtpHandle);

			if (eRetCode != CURLE_OK)
			{
				continue;
			}

			curl_easy_setopt(m_hFtpHandle, CURLOPT_NOBODY, 0L);

			curl_easy_setopt(m_hFtpHandle, CURLOPT_HEADER, 0L);

			fseek(pFile, static_cast<long>(iLocalFileLength), SEEK_SET);

			curl_easy_setopt(m_hFtpHandle, CURLOPT_APPEND, 1L);
		}
		else
		{
			curl_easy_setopt(m_hFtpHandle, CURLOPT_APPEND, 0L);
		}

		eRetCode = curl_easy_perform(m_hFtpHandle);
	}

	// Finish the uploading
	fclose(pFile);

	if (eRetCode != CURLE_OK)
	{
		std::string strErrorMessage = GetCurlErrorInfo(eRetCode);

		SetErrorInfo(eRetCode, strErrorMessage);

		// Destory the url
		DestoryUrL(m_hFtpHandle);

		return false;
	}

	return true;
}

// Build remote file path
TyzLibCurl::Boolean TyzLibCurl::BuildFilePath(std::string strFilePath,
	std::string strDirPath,
	std::string& strFinalFilePath)
{
	if (strFilePath.empty())
	{
		return false;
	}

	if (strDirPath.empty())
	{
		return false;
	}

	std::string strPath = Directory::TrimEnding(strFilePath);

	int iPos = strPath.find_last_of('/');

	if (iPos == -1)
	{
		return false;
	}

	std::string strName = strPath.substr(iPos + 1);

	std::string strRemoteDirPath = Directory::AddEnding(strDirPath);

	std::string strFinalPath = strRemoteDirPath + strName;

	strFinalFilePath = strFinalPath;

	return true;
}

// Ftp upload directory
bool TyzLibCurl::FtpUploadDir(const std::string strRemoteDirPath, const std::string strLocalDirPath)
{
	if (strRemoteDirPath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Remote directory path is empty !");

		return false;
	}

	if (strLocalDirPath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Local directory path is empty !");

		return false;
	}

	if (!Directory::IsDirectory(strLocalDirPath))
	{
		std::string strErrorMsg = std::string("It is not a valid local directory[%s]") + strLocalDirPath;

		SetErrorInfo(CURLE_UNKNOWN_OPTION, strErrorMsg);

		return false;
	}

	// Get files from the local directory
	std::vector<std::string> FileTable;

	if (!Directory::GetAllFiles(strLocalDirPath, FileTable))
	{
		std::string strErrorMsg = std::string("Failed to get any file under the directory[%s]") + strLocalDirPath;

		SetErrorInfo(CURLE_UNKNOWN_OPTION, strErrorMsg);

		return false;
	}

	// Upload files
	bool bIsUploadAllOk = true;

	for (int index = 0; index < static_cast<int>(FileTable.size()); ++index)
	{
		std::string strFilePath = FileTable[index];

		if (!FtpUpload(strRemoteDirPath, strFilePath))
		{
			std::string strErrorMsg = GetErrorMsg();

			SetErrorInfo(CURLE_UNKNOWN_OPTION, strErrorMsg);

			bIsUploadAllOk = false;

			continue;
		}
	}

	return bIsUploadAllOk;
}

// Ftp download directory
bool TyzLibCurl::FtpDownloadDir(const std::string strRemoteDirPath, const std::string strLocalDirPath)
{
	if (strRemoteDirPath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Remote directory path is empty !");

		return false;
	}

	if (strLocalDirPath.empty())
	{
		SetErrorInfo(CURLE_UNKNOWN_OPTION, "Local directory path is empty !");

		return false;
	}

	// Get all files under the remote directory
	std::vector<FileInfo> FileInfoTable;

	if (!FtpList(strRemoteDirPath, FileInfoTable))
	{
		std::string strErrorMsg = std::string("Failed to list remote directory's file[%s]") + strRemoteDirPath;

		SetErrorInfo(CURLE_UNKNOWN_OPTION, strErrorMsg);

		return false;
	}

	// Download files
	bool bIsDownloadAllOk = true;

	for (int index = 0; index < static_cast<int>(FileInfoTable.size()); ++index)
	{
		FileInfo Info = FileInfoTable[index];

		if (Info.bIsDirectory)
		{
			// Build local directory name
			std::string strLocalPath = strLocalDirPath;

			// Make sure that your input is GBK
			std::string strFileName = GBK::GetString(Info.strFileName, ENCODE_UTF8);

			std::string strFinalLocalDirPath = Directory::AddEnding(strLocalPath) + strFileName;

			// Make sure that your input is GBK
			std::string strRemoteDirPath = GBK::GetString(Info.strFilePath, ENCODE_UTF8);

			if (!FtpDownloadDir(strRemoteDirPath, strFinalLocalDirPath))
			{
				return false;
			}
		}
		else
		{
			// Make sure that your input is GBK
			std::string strFilePath = GBK::GetString(Info.strFilePath, ENCODE_UTF8);

			if (!FtpDownload(strFilePath, strLocalDirPath))
			{
				std::string strErrorMsg = GetErrorMsg();

				SetErrorInfo(CURLE_UNKNOWN_OPTION, strErrorMsg);

				bIsDownloadAllOk = false;

				continue;
			}
		}
	}

	return bIsDownloadAllOk;
}
